package com.hex.ds.hdrs.common.aop;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.hex.bigdata.hbda.service.UserService;
import com.hex.bigdata.hbda.service.dto.UserDto;
import com.hex.ds.hdrs.app.converter.IAppAdminRelationConverter;
import com.hex.ds.hdrs.app.dto.AppAdminRelationDto;
import com.hex.ds.hdrs.app.po.AppAdminRelationPo;
import com.hex.ds.hdrs.app.po.AppConf;
import com.hex.ds.hdrs.app.service.IAppAdminRelationService;
import com.hex.ds.hdrs.app.service.impl.AppConfService;
import com.hex.ds.hdrs.common.annotation.AppCodeVerification;
import com.hex.ds.hdrs.common.utils.BaseUtil;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Package: com.hex.hdc.center.app.common.aop
 * @ClassName AppCodeVerificationAspect
 * @Description: 应用权限验证
 * @Author: wz.li
 * @Date 2022/8/12 14:28
 * @Version v2.0.2
 **/
@Component
@Aspect
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class AppCodeVerificationAspect {
    // 系统管理员角色ID
    private final static long SYSTEM_ROLE_ID = 1;

    @Autowired
    private UserService userService;

    @Autowired
    private AppConfService appConfService;

    @Autowired
    private IAppAdminRelationService appAdminRelationService;

    @Autowired
    private IAppAdminRelationConverter appAdminRelationConverter;

    @Before("@annotation(com.hex.ds.hdrs.common.annotation.AppCodeVerification)")
    public void before(JoinPoint joinPoint) {
        if (!isSystemAdministrator()) {
            Class<?> paramClass = getParamClass(joinPoint);
            Map<Class<?>, Object> params = beforCheck(paramClass, joinPoint);
            String appCode = getAppCode(paramClass, params);
            afterCheck(appCode);
        }
    }

    /*
     * @Method: getAppCode <br>
     * @Param: [paramClass, params] <br>
     * @Return: java.lang.String <br>
     * @Description：获取参数应用编码<br>
     * @Author： wz.li<br>
     * @Date： 2022/11/22 14:17 <br>
     * @Version： V2.0.2<br>
     */
    private String getAppCode(Class<?> paramClass, Map<Class<?>, Object> params) {
        if ("java.lang.String".equals(paramClass.getName())) {
            return (String) params.get(paramClass);
        } else {
            JSONObject param = (JSONObject) JSONObject.toJSON(params.get(paramClass));
            return param.containsKey("appCode") ? param.getString("appCode") : "";
        }
    }

    /*
     * @Method: beforCheck <br>
     * @Param: [paramClass, params] <br>
     * @Return: void <br>
     * @Description：前置判断<br>
     * @Author： wz.li<br>
     * @Date： 2022/8/12 14:28 <br>
     * @Version： V2.0.2<br>
     */
    private Map<Class<?>, Object> beforCheck(Class<?> paramClass, JoinPoint joinPoint) {
        if (joinPoint.getArgs() == null || joinPoint.getArgs()[0] == null) {
            throw new RuntimeException("未传递验证参数");
        } else if (paramClass == null) {
            throw new RuntimeException("未指定校验参数对象");
        }
        Map<Class<?>, Object> params = Arrays.asList(joinPoint.getArgs()).stream().collect(Collectors.toMap(k -> k.getClass(), v -> v));
        if (CollectionUtil.isEmpty(params) || !params.containsKey(paramClass)) {
            throw new RuntimeException("未匹配到指定的校验参数对象");
        }
        return params;
    }

    /*
     * @Method: afterCheck <br>
     * @Param: [appCode, appName] <br>
     * @Return: void <br>
     * @Description：后置判断<br>
     * @Author： wz.li<br>
     * @Date： 2022/8/12 14:28 <br>
     * @Version： V2.0.2<br>
     */
    private void afterCheck(String appCode) {
        if (StrUtil.isBlank(appCode)) {
            throw new RuntimeException("应用编码不允许为空");
        } else if (!isCurrentUserOperationPermission(appCode)) {
            throw new RuntimeException("应用编码异常，你没有操作该应用的权限");
        }
    }

    /*
     * @Method: isSystemAdministrator <br>
     * @Param: [] <br>
     * @Return: boolean <br>
     * @Description：当前用户是否是系统管理员<br>
     * @Author： wz.li<br>
     * @Date： 2022/8/12 14:36 <br>
     * @Version： V2.0.2<br>
     */
    private boolean isSystemAdministrator() {
        UserDto userDto = userService.findById(BaseUtil.getUser().getJwtUser().getId());
        List<Long> roles = userDto.getRoles().stream().map(t -> t.getId()).collect(Collectors.toList());
        return roles.contains(SYSTEM_ROLE_ID);
    }

    /*
     * @Method: isCurrentUserOperationPermission <br>
     * @Param: [appCode] <br>
     * @Return: boolean <br>
     * @Description：当前用户是否有操作指定应用的权限<br>
     * @Author： wz.li<br>
     * @Date： 2022/8/12 14:36 <br>
     * @Version： V2.0.2<br>
     */
    private boolean isCurrentUserOperationPermission(String appCode) {
        AppConf appConf = appConfService.qryAppConfByAppCode(appCode);
        if (appConf != null && StrUtil.isNotBlank(appConf.getAppAdmin()) && appConf.getAppAdmin().equals(BaseUtil.getUserId())) {
            return true;
        }
        AppAdminRelationDto appAdminRelationDto = appAdminRelationConverter.AppCodeAndAdminUserCodeToAppAdminRelationDto(appCode);
        List<AppAdminRelationPo> appUserRelevances = appAdminRelationService.queryList(appAdminRelationDto);
        return CollectionUtil.isNotEmpty(appUserRelevances);
    }

    /*
     * @Method: getParamClass <br>
     * @Param: [joinPoint] <br>
     * @Return: java.lang.Class<?> <br>
     * @Description：获取验证参数对象Class<br>
     * @Author： wz.li<br>
     * @Date： 2022/8/12 14:29 <br>
     * @Version： V2.0.2<br>
     */
    private Class<?> getParamClass(JoinPoint joinPoint) {
        MethodSignature sign = (MethodSignature) joinPoint.getSignature();
        Method method = sign.getMethod();
        AppCodeVerification appCodeVerification = method.getAnnotation(AppCodeVerification.class);
        return appCodeVerification.paramObject();
    }
}
